#!/usr/bin/env python
# -*- coding: UTF-8 -*-
'''
@Project ：backend 
@File    ：minio_client.py
@IDE     ：PyCharm 
@Author  ：tongweiqing1015@163.com
@Date    ：2025/10/17 上午11:50 
'''
import datetime

from minio import Minio
from minio.error import S3Error
from io import BytesIO
import os

endpoint = "minapi.huaxianguan.com"
access_key = "admin"
secret_key = "admin123456"


class MinioClient:
    """
    MinIO 客户端
    自建oss存储
    """

    def __init__(self, endpoint, access_key, secret_key, bucket_name, secure=False):
        """
        初始化 MinIO 客户端，并固定 bucket
        :param endpoint: MinIO 地址
        :param access_key: 用户名
        :param secret_key: 密码
        :param bucket_name: 固定使用的 bucket
        :param secure: 是否启用 https
        """
        self.client = Minio(endpoint, access_key=access_key, secret_key=secret_key, secure=secure)
        self.bucket_name = bucket_name
        # 自动创建 bucket
        if not self.client.bucket_exists(bucket_name):
            self.client.make_bucket(bucket_name)

    def upload_file(self, object_name, file_path, content_type=None):
        """上传文件"""
        try:
            self.client.fput_object(self.bucket_name, object_name, file_path, content_type=content_type)
        except S3Error as e:
            raise RuntimeError(f"上传文件 {file_path} 到 {self.bucket_name}/{object_name} 失败: {e}")

    def upload_bytes(self, object_name, data: bytes, content_type="application/octet-stream"):
        """上传二进制数据"""
        try:
            self.client.put_object(
                self.bucket_name, object_name, BytesIO(data), length=len(data), content_type=content_type
            )
        except S3Error as e:
            raise RuntimeError(f"上传字节数据到 {self.bucket_name}/{object_name} 失败: {e}")

    def download_file(self, object_name, file_path):
        """下载文件到本地"""
        try:
            self.client.fget_object(self.bucket_name, object_name, file_path)
        except S3Error as e:
            raise RuntimeError(f"下载 {self.bucket_name}/{object_name} 到 {file_path} 失败: {e}")

    def download_bytes(self, object_name) -> bytes:
        """下载文件到内存"""
        try:
            response = self.client.get_object(self.bucket_name, object_name)
            data = response.read()
            response.close()
            response.release_conn()
            return data
        except S3Error as e:
            raise RuntimeError(f"下载 {self.bucket_name}/{object_name} 失败: {e}")

    def delete_object(self, object_name):
        """删除文件"""
        try:
            self.client.remove_object(self.bucket_name, object_name)
        except S3Error as e:
            raise RuntimeError(f"删除 {self.bucket_name}/{object_name} 失败: {e}")

    def list_objects(self, prefix="", recursive=True):
        """列举对象"""
        try:
            return [obj.object_name for obj in self.client.list_objects(self.bucket_name, prefix=prefix, recursive=recursive)]
        except S3Error as e:
            raise RuntimeError(f"列举 {self.bucket_name} 下对象失败: {e}")

    def presigned_get_url(self, object_name, expires=datetime.timedelta(seconds=3600)):
        """生成临时下载链接"""
        try:
            return self.client.presigned_get_object(self.bucket_name, object_name, expires=expires)
        except S3Error as e:
            raise RuntimeError(f"生成下载链接失败: {e}")

    def presigned_put_url(self, object_name, expires=datetime.timedelta(seconds=3600)):
        """生成临时上传链接"""
        try:
            return self.client.presigned_put_object(self.bucket_name, object_name, expires=expires)
        except S3Error as e:
            raise RuntimeError(f"生成上传链接失败: {e}")

    # ================= 批量操作文件夹 =================
    def upload_folder(self, local_folder, remote_folder=""):
        """上传本地目录到 MinIO"""
        if not os.path.isdir(local_folder):
            raise ValueError(f"{local_folder} 不是目录")
        for root, _, files in os.walk(local_folder):
            for file in files:
                local_path = os.path.join(root, file)
                relative_path = os.path.relpath(local_path, local_folder)
                object_name = os.path.join(remote_folder, relative_path).replace("\\", "/")
                self.upload_file(object_name, local_path)

    def download_folder(self, remote_folder, local_folder):
        """下载 MinIO 前缀到本地目录"""
        objects = self.list_objects(prefix=remote_folder, recursive=True)
        for obj_name in objects:
            relative_path = os.path.relpath(obj_name, remote_folder).replace("\\", "/")
            local_path = os.path.join(local_folder, relative_path)
            os.makedirs(os.path.dirname(local_path), exist_ok=True)
            self.download_file(obj_name, local_path)


minio_client = MinioClient(endpoint, access_key, secret_key, bucket_name="anmo", secure=False)

# ================= 示例 =================
if __name__ == "__main__":
    # 上传文件
    minio_client.upload_file("/test/minio_client.py", "minio_client.py")

    # 下载文件
    minio_client.download_file("/test/minio_client.py", "minio_client3.py")

    # 批量上传目录
    minio_client.upload_folder(os.getcwd(), "remote_folder")

    # 批量下载目录
    minio_client.download_folder("remote_folder", "download_folder2")

    # 列举对象
    print(minio_client.list_objects())

    # 删除文件
    minio_client.delete_object("test.txt")

    # 临时下载 URL
    url = minio_client.presigned_get_url("/test/minio_client.py")
    print(url)
